from datetime import datetime, timedelta
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from sqlalchemy.orm import Session
import os
from dotenv import load_dotenv

from .models.schemas import TokenData
from .models.models import User
from .database import get_db

load_dotenv()

SECRET_KEY = os.getenv("SECRET_KEY")
ALGORITHM = os.getenv("ALGORITHM", "HS256")
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

/**
 * @file auth.py
 * @brief Authentication and authorization utilities for Meeting Management System.
 * @author Ziyi Wang
 *
 * Provides JWT token creation, password hashing, user authentication, and permission dependencies.
 */

/**
 * @brief Verify a plain password against a hashed password.
 * @param plain_password The plain text password.
 * @param hashed_password The hashed password.
 * @return True if match, else False.
 */
def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

/**
 * @brief Hash a password.
 * @param password The plain text password.
 * @return The hashed password.
 */
def get_password_hash(password):
    return pwd_context.hash(password)

/**
 * @brief Authenticate a user by username and password.
 * @param db Database session.
 * @param username Username.
 * @param password Password.
 * @return User object if valid, else False.
 */
def authenticate_user(db: Session, username: str, password: str):
    user = db.query(User).filter(User.username == username).first()
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

/**
 * @brief Create a JWT access token.
 * @param data Data to encode in token.
 * @param expires_delta Expiration timedelta.
 * @return Encoded JWT token.
 */
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

/**
 * @brief Dependency to get the current user from JWT token.
 * @param token JWT token.
 * @param db Database session.
 * @return User object.
 * @exception HTTPException 401 if token invalid or user not found.
 */
async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无效的身份凭证",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except JWTError:
        raise credentials_exception
    user = db.query(User).filter(User.username == token_data.username).first()
    if user is None:
        raise credentials_exception
    return user

/**
 * @brief Dependency to get the current active user.
 * @param current_user User object.
 * @return User object if active.
 * @exception HTTPException 400 if user is inactive.
 */
async def get_current_active_user(current_user: User = Depends(get_current_user)):
    if not current_user.is_active:
        raise HTTPException(status_code=400, detail="用户已停用")
    return current_user

/**
 * @brief Dependency to get the current admin user.
 * @param current_user User object.
 * @return User object if admin.
 * @exception HTTPException 403 if not admin.
 */
async def get_admin_user(current_user: User = Depends(get_current_user)):
    if not current_user.is_admin:
        raise HTTPException(status_code=403, detail="没有足够的权限")
    return current_user 